﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Interop;
using SlimDX.Direct3D9;
using System.Windows;
using System.Runtime.InteropServices;
using System.Diagnostics.CodeAnalysis;

namespace System.Windows.Media.Media3D10
{
    /// <summary>
    /// Surface de rendu DirectX10
    /// </summary>
    public class D3D10Image : D3DImage
    {
        #region Constructors
        /// <summary>
        /// Initialise une nouvelle instance de la classe <see cref="D3D10Image"/>
        /// </summary>
        public D3D10Image()
        {
            _isDrawable = false;
            NumActiveImages++;
        }

        /// <summary>
        /// Destructeur de l'instance actuelle
        /// </summary>
        ~D3D10Image()
        {
            SetBackBufferSlimDX(null);
        }
        #endregion

        #region Data
        private bool _isDrawable;
        #endregion

        #region Static Members
        private static int NumActiveImages = 0;
        #endregion

        #region Public Members
        /// <summary>
        /// Invalide la surface de rendu actuelle.
        /// </summary>
        public void InvalidateD3DImage()
        {
            if (_isDrawable)
            {
                Lock();
                AddDirtyRect(new Int32Rect(0, 0, PixelWidth, PixelHeight));
                Unlock();
            }
        }

        /// <summary>
        /// Affecte le backbuffer à la surface de dessin
        /// </summary>
        /// <param name="texture"><see cref="SlimDX.Direct3D10.Texture2D"/> qui sert de backbuffer à la surface de dessin.</param>
        public void SetBackBufferSlimDX(SlimDX.Direct3D10.Texture2D texture)
        {
            if (texture == null)
            {
                _isDrawable = false;
                Lock();
                SetBackBuffer(D3DResourceType.IDirect3DSurface9, IntPtr.Zero);
                Unlock();
            }
            else if (IsShareable(texture))
            {
                _isDrawable = true;
                Format format = TranslateFormat(texture);
                if (format == Format.Unknown)
                    throw new ArgumentException("Texture format is not compatible with OpenSharedResource", "texture");

                IntPtr Handle = GetSharedHandle(texture);
                if (Handle == IntPtr.Zero)
                    throw new ArgumentNullException("texture", "Texture handle cannot be null");

                using (Texture sharedTexture = new Texture(Engine.Device9, texture.Description.Width, texture.Description.Height, 1, Usage.RenderTarget, format, Pool.Default, ref Handle))
                {
                    using (Surface Surface = sharedTexture.GetSurfaceLevel(0))
                    {
                        Lock();
                        SetBackBuffer(D3DResourceType.IDirect3DSurface9, Surface.ComPointer);
                        Unlock();
                    }
                }
            }
            else
                throw new ArgumentException("Texture must be created with ResourceOptionFlags.Shared");
        }
        #endregion

        #region Private Members
        private static IntPtr GetSharedHandle(SlimDX.Direct3D10.Texture2D Texture)
        {
            SlimDX.DXGI.Resource resource = new SlimDX.DXGI.Resource(Texture);
            IntPtr result = resource.SharedHandle;

            resource.Dispose();

            return result;
        }

        private static Format TranslateFormat(SlimDX.Direct3D10.Texture2D Texture)
        {
            switch (Texture.Description.Format)
            {
                case SlimDX.DXGI.Format.R10G10B10A2_UNorm:
                    return SlimDX.Direct3D9.Format.A2B10G10R10;

                case SlimDX.DXGI.Format.R16G16B16A16_Float:
                    return SlimDX.Direct3D9.Format.A16B16G16R16F;

                case SlimDX.DXGI.Format.B8G8R8A8_UNorm:
                    return SlimDX.Direct3D9.Format.A8R8G8B8;

                default:
                    return SlimDX.Direct3D9.Format.Unknown;
            }
        }

        private static bool IsShareable(SlimDX.Direct3D10.Texture2D Texture)
        {
            return (Texture.Description.OptionFlags & SlimDX.Direct3D10.ResourceOptionFlags.Shared) != 0;
        }
        #endregion
    }
}
